package com.hisense.hiioc.hismartplate.server.handler;

import com.alibaba.fastjson.JSONObject;
import com.hisense.hiioc.hismartplate.constants.ServerConstants;
import com.hisense.hiioc.hismartplate.server.CoreManager;
import com.hisense.hiioc.hismartplate.server.ProtoBuilder;
import com.hisense.hiioc.hismartplate.utils.NettyUtils;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Author: almo
 * @Date: 2018/12/5 下午4:14
 * @Version 1.0
 */
public class AuthHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger logger = LoggerFactory.getLogger(AuthHandler.class);
    private WebSocketServerHandshaker handShaker;
    private CoreManager coreManager = CoreManager.getInstance();

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) {
        if (msg instanceof FullHttpRequest) {
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            handleWebSocket(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent ise = (IdleStateEvent) evt;
            if (ise.state().equals(IdleState.READER_IDLE)) {
                final String remoteAddress = NettyUtils.parseChannelRemoteAddr(ctx.channel());
                logger.warn("NETTY SERVER PIPELINE: IDLE exception [{}]", remoteAddress);
                coreManager.removeChannel(ctx.channel());
            }
        }
        ctx.fireUserEventTriggered(evt);
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        if (!request.decoderResult().isSuccess() || !"websocket".equals(request.headers().get("Upgrade"))) {
            logger.warn("proto buf don't support webSocket");
            ctx.channel().close();
            return;
        }
        WebSocketServerHandshakerFactory handShakerFactory = new WebSocketServerHandshakerFactory(
                ServerConstants.WEB_SOCKET_URL, null, true);
        handShaker = handShakerFactory.newHandshaker(request);
        if (handShaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            final Channel channel = ctx.channel();
            handShaker.handshake(channel, request);
            coreManager.addChannel(channel, request.headers());
        }
    }

    private void handleWebSocket(ChannelHandlerContext ctx, WebSocketFrame frame) {
        // 判断是否关闭链路命令
        if (frame instanceof CloseWebSocketFrame) {
            handShaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            coreManager.removeChannel(ctx.channel());
            return;
        }
        // 判断是否Ping消息
        if (frame instanceof PingWebSocketFrame) {
            System.out.println("ping------");
            logger.info("ping message:{}", frame.content().retain());
            ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // 判断是否Pong消息
        if (frame instanceof PongWebSocketFrame) {
            System.out.println("pong------");
            logger.info("pong message:{}", frame.content().retain());
            ctx.writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
            return;
        }

        // 本程序目前只支持文本消息
        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException(frame.getClass().getName() + " frame type not supported");
        }

        String message = ((TextWebSocketFrame) frame).text();
        JSONObject json = JSONObject.parseObject(message);
        int code = json.getInteger("protoCode");
        Channel channel = ctx.channel();
        switch (code) {
            case ProtoBuilder.PING_PROTO:
                coreManager.updateDevClientTime(channel);
                coreManager.sendPong(ctx.channel());
                logger.info("receive ping message, iPAddress: {}", NettyUtils.parseChannelRemoteAddr(channel));
                return;
            case ProtoBuilder.PONG_PROTO:
                coreManager.updateDevClientTime(channel);
                logger.info("receive pong message, iPAddress: {}", NettyUtils.parseChannelRemoteAddr(channel));
                return;
            case ProtoBuilder.AUTH_PROTO:
                coreManager.authorizeChannel(channel);
                //coreManager.sendInfo(channel, ServerConstants.ServerCode.SYS_AUTH_STATE, isSuccess);
//                if (isSuccess) {
//                    //  coreManager.broadCastInfo(ServerConstants.ServerCode.SYS_USER_COUNT, coreManager.getAuthUserCount());
//                }
                return;
            case ProtoBuilder.MESS_PROTO:
            case ProtoBuilder.SYST_PROTO:
            case ProtoBuilder.EROR_PROTO:
                break;
            default:
                logger.warn("The code [{}] can't be auth!!!", code);
                return;
        }
        ctx.fireChannelRead(frame.retain());
    }
}
